package com.huaxin.dss2.data;

import com.google.common.collect.Maps;
import com.huaxin.dss2.data.bean.DataInfo;
import com.huaxin.dss2.data.bean.DataValue;
import com.huaxin.dss2.data.bean.DssBox;
import com.huaxin.dss2.data.dao.base.BaseDataDao;
import com.huaxin.dss2.data.dao.DataDaoFactory;
import com.huaxin.dss2.data.exception.jdbc.DataSourceNotFoundException;
import com.huaxin.dss2.message.bean.DataMsgBean;
import com.huaxin.dss2.util.Constant;
import com.huaxin.dss2.util.Constant.DssStateType;
import com.huaxin.dss2.util.MathUtil;
import com.huaxin.dss2.util.ScheduledValue;
import com.huaxin.dss2.util.SignalRUtils;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import javax.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;


/**
 * Created by HX-119 on 2014/6/30.
 */
@Component
@DependsOn({"dataDaoFactory"})
@Slf4j
public class DataService {

    private static Map<String,Long>  mapCountStationStartTime = Maps.newHashMap();

    @Autowired
    private EhCacheCacheManager ehCacheCacheManager;

    private Cache savedStationDataTimes;
    private Cache dataInfoCache;

    private BaseDataDao baseDataDao;

    @PostConstruct
    private void init() throws Exception {
        savedStationDataTimes = ehCacheCacheManager.getCache("savedStationDataTimes");
        dataInfoCache = ehCacheCacheManager.getCache("dataInfo");
        baseDataDao = DataDaoFactory.createDataDao();
        baseDataDao.init();
    }

    /**
     * 格式化指标的值
     */
    public DataMsgBean updateDataMsgBean(DataMsgBean dataMsgBean) {

        if (dataMsgBean.getStationId() == null) {
            return null;
        }

        Map<String,DataInfo> dataInfoMap = baseDataDao.findDataInfoMap(dataMsgBean.getStationId());
        Map<String, Double> dataMap = dataMsgBean.getDataMap();
        for (Entry<String, DataInfo> entry : dataInfoMap.entrySet()) {
            String dataCode = entry.getKey();
            DataInfo dataInfo = dataInfoMap.get(dataCode);
            if (!StringUtils.isEmpty(dataInfo.getFormula())) {
                try {
                    Double formulaValue = MathUtil.getFormulaValue(dataInfo.getFormula(), dataMap);
                    if (formulaValue != null) {
                        dataMap.put(dataCode, formulaValue);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    log.warn("dataCode[{}]公式计算错误", dataInfo.getFullDataCode());
                }

            }

            dataMsgBean.setDataMap(dataMap);

        }

        // 将值的小数位长度进行格式化
        for (Map.Entry<String,Double> data: dataMap.entrySet()){
            Integer intFrac = ScheduledValue.VALUE_FRAC_DIGITS_MAP.get(data.getKey());
            Integer floatFrac = ScheduledValue.FLOAT_FRAC_DIGITS_MAP.get(data.getKey());
            if (intFrac != null) {
                //小数长度处理
                data.setValue(data.getValue() / Math.pow(10, intFrac));
            }

            if (floatFrac != null){
                //浮点型进行格式化
                data.setValue(Double.parseDouble(String.format("%."+floatFrac+"f",data.getValue())));
            }
        }
        dataMsgBean.setDataMap(dataMap);

        return dataMsgBean;
    }

    public void save(DataMsgBean dataMsgBean) throws Exception {
        Map<String, DataInfo> dataInfoMap = baseDataDao.findDataInfoMap(dataMsgBean.getStationId());
        Map<String, Double> dataMap = dataMsgBean.getDataMap();
        log.info("站点{}save方法执行",dataMsgBean.getStationId());
        List<DataValue> saveList = new ArrayList<>();
        for (Map.Entry<String, Double> entry : dataMap.entrySet()) {
            String dataCode = entry.getKey();
            DataInfo dataInfo = dataInfoMap.get(dataCode);

            if (null == dataInfo && !dataCode.contains("iswarn")) {
                log.warn("非法的dataCode[{}]跳过", dataCode);
                continue;
            }

            if (Objects.equals(Constant.getProperties().getCompanyName(),"bsy")){
                if (null == dataInfo || System.currentTimeMillis() < dataInfo.getNextTime()){
                    log.info("指标{}不存在或还未到达存储时间",dataCode);
                    continue;
                }
                //更新最新保存时间
                dataInfo.updateLastTime();
            }

            saveList.add(new DataValue(entry.getKey(),entry.getValue()));
        }

        Date dataDate = DateUtils.truncate(new Date((dataMsgBean.getDataTime())),Calendar.MINUTE);
        if (saveList.size() > 0) {
            try {
                if (isNotSaved(dataMsgBean.getStationId(), dataDate)) {
                    dataDate = DateUtils.truncate(new Date(dataMsgBean.getDataTime()),Calendar.MINUTE);

                    if (Objects.equals("jiajing",Constant.getProperties().getCompanyName())){
                        //从嘉靖特殊指标map中取出放入saveList进行同时保存
                        saveList.add(ScheduledValue.DATA_IS_WARN_MAP
                            .get(getStationCode(saveList.get(0).getDataCode(), Constant.getFullCode().getStationIndex())));
                    }

                    baseDataDao.save(dataMsgBean.getStationId(),saveList, dataDate);
                }
            } catch (DataSourceNotFoundException e) {
                log.error("对应数据源[datadb_{}]未找到.", dataMsgBean.getStationId());
            }
        }
    }

    public void flushBox(){
        List<DssBox> changeBox = baseDataDao.findChangeBox();

        for (DssBox dssBox : changeBox){
            if (Objects.equals(dssBox.getType(), DssStateType.DATA_INFO)){
                baseDataDao.refreshDataInfo(dssBox.getStationCode(),DssStateType.DATA_INFO);
            }

            if (Objects.equals(dssBox.getType(),DssStateType.START_BOX)){
                SignalRUtils.startBox(Long.parseLong(dssBox.getBoxUid()));
                baseDataDao.refreshDataInfo(dssBox.getStationCode(),DssStateType.START_BOX);
            }
        }
    }

    private String getStationCode(String dataCode, Integer stationIndex) {
        String[] split = dataCode.split("_");
        if (split.length < stationIndex) {
            return "";
        }
        return split[stationIndex];
    }

    private boolean isNotSaved(String stationId, Date dataDate) {
        String cacheKey = stationId + '-' + dataDate.getTime();
        Cache.ValueWrapper cacheValue = savedStationDataTimes
            .get(stationId + '-' + dataDate.getTime());
        if (cacheValue == null) {
            savedStationDataTimes.put(cacheKey, true);
            return true;
        }
        return false;
    }

    @Cacheable(value = "saveTime", key = "#dataTime+'-'+#storageInterval")
    public boolean isSaveTime(long dataTime, int storageInterval) {
        long timeMillisInDay =
            dataTime - DateUtils.truncate(new Date(dataTime), Calendar.DATE).getTime();
        return (((timeMillisInDay / 1000) / storageInterval == 1) && (
            (timeMillisInDay / 1000) % storageInterval < 300));
    }

    @Cacheable(value = "saveTime", key = "#dataTime+'-'+#storageInterval")
     public boolean inSaveTime(long dataTime, int storageInterval) {
        long timeMillisInDay =
            dataTime - DateUtils.truncate(new Date(dataTime), Calendar.DATE).getTime();
        if (storageInterval == 86400) {
            return (timeMillisInDay / 1000) % storageInterval < 300;
        }
        return (timeMillisInDay / 1000) % storageInterval < 60;
    }

    @Cacheable(value = "dataInfo", key = "#fullDataCode")
    public DataInfo findDataInfo(String fullDataCode) {

        Cache.ValueWrapper cacheValue = dataInfoCache.get(fullDataCode);
        if (cacheValue == null) {
            String[] fullDataCodeArray = fullDataCode.split("_");
            if (fullDataCodeArray.length < Constant.getFullCode().getStationIndex()) {
                return null;
            }
            DataInfo dataInfo = baseDataDao.findDataInfo(fullDataCodeArray[0],Long.valueOf(fullDataCodeArray[1]),fullDataCodeArray[2]);
            if (dataInfo != null) {
                dataInfoCache.put(fullDataCode, dataInfo);
                return dataInfo;
            }
            return null;
        } else {
            return (DataInfo) cacheValue.get();
        }
    }


    public int getState(String fullDataCode, double doubleValue) {
        DataInfo dataInfo = findDataInfo(fullDataCode);
        if (null == dataInfo) {
            return 0;
        }
        if (doubleValue > dataInfo.getLowLimit() && doubleValue < dataInfo.getHighLimit()) {
            return 0;
        } else if (doubleValue >= dataInfo.getHighLimit() && doubleValue < dataInfo.getMaxLimit()) {
            return 1;
        } else if (doubleValue <= dataInfo.getLowLimit() && doubleValue > dataInfo.getMinLimit()) {
            return -1;
        } else if (doubleValue >= dataInfo.getMaxLimit()) {
            return 2;
        } else if (doubleValue <= dataInfo.getMinLimit()) {
            return -2;
        } else {
            throw new RuntimeException(
                "[" + fullDataCode + "]数据[" + doubleValue + "]跟数据状态[" + dataInfo.getMinLimit() + ","
                    + dataInfo.getLowLimit() + "," + dataInfo.getHighLimit() + "," + dataInfo
                    .getMaxLimit() + "]无法匹配");
        }
    }

    public List<String> findSmallStationCodes() {
        return baseDataDao.findSmallStationCodes();
    }
}
